/* Copyright (C) 2016-2017 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved

 * File:lktencrypt.cpp
 * Author:guojianchuan
 * Date:2017-06-29

 */
 
/* 系统头文件 */
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/shm.h>
#include <iostream>
#include <memory>
#include <functional>
#include <algorithm>

/* 依赖库头文件 */
#include <sqlrelay/sqlrserver.h>


/* 本项目其他模块头文件 */
#include "lkterr.h"
#include "lktcommon.h"
#include "lktstruct.h"
#include "lktsqlparser.h"
#include "lktcipher.h"
#include "lktshm.h"
#include "zlog.h"
#include "character.h"
#include "sqlfield.h"


#define ZLOG_SQLRELAY_CONF              INSTALL_DIR"dbs/conf/sqlrelay-zlog.conf"
#define ZLOG_SQLRELAY_CAT               "sqlrelay_cat"
#define APP_CIPHER_MAPPING_ID           9999    /* 应用级别加密默认的MAPPINGID是9999 */


static const char *supportStatement[] = {
    "select",
    "update",
    "insert",
    "replace",
    "delete"
};

class sqlrtranslation_lktencrypt : public sqlrtranslation {
    public:
            sqlrtranslation_lktencrypt(
                    sqlrservercontroller *cont,
                    sqlrtranslations *sqlts,
                    xmldomnode *parameters);
            ~sqlrtranslation_lktencrypt();
            
        bool    run(sqlrserverconnection *sqlrcon,
                    sqlrservercursor *sqlrcur,
                    const char *query,
                    stringbuffer *translatedquery);
        

    private:
        bool isDataRefreshDecryptIdentify (const char* sql);
        bool isSignleDataDecryptIdentify (const char* sql);
        bool isExistCharTypeValue (SQLPARSER_S *sqlparser);
        
        int encryptAndRebuildSql (SQLPARSER_S *sqlparser, stringbuffer& rebuildSql);
        
        char *findMatchColumninDBInstance (int columnIdx, char* table, char* db, char* columnName);
        COLUMN_STRATEGY_S* findStrategy (char* column, char* table, char *db);
        COLUMN_STRATEGY_S* getMatchedStrategy (SQLPARSER_COLUMN_NODE_S *node);
        bool isSupportedStatement (const char* sql);
        int getStragegyCharset (COLUMN_STRATEGY_S* strategy);
        int decryptAndRebuildSql (SQLPARSER_S *sqlparser, stringbuffer& rebuildSql);

	int internal_encryptvalue (
            const std::string& input, 
            std::string& output,
            lktshm& share);
        int normal_formatvalue(
            const std::string& b64,
            std::string& output); 
        int like_formatvalue(
            const std::string& data,
            std::string& output,
            std::string& escape); 

        typedef std::function<int (const std::string&, std::string&)> encryptor;

        int normal_encryptvalue (const std::string& input, std::string& output);
        int search_like_encryptvalue(
            const std::string& input, std::string& output, std::string escape);
        int search_other_encryptvalue(
            const std::string& input, std::string& output);

        encryptor getEncryptor( SQLPARSER_COLUMN_NODE_S *node, COLUMN_STRATEGY_S *strategy);

        bool isContainOnlyWildCard  (char* value);

        typedef std::function<int (std::string&, stringbuffer&)> 
            Decryptor;

        Decryptor getDecryptor(const std::string& input);

        int decrypt (
                const std::string& input, 
                stringbuffer& output,
                const char* key,
                const char* iv);
        int decrypt_searchable (
                const std::string& input, 
                stringbuffer& output,
                const char* key, 
                const char* iv);
    private:
        
        stringbuffer decryptoutput;        

        /* 当前连接正在使用的数据库 */
        char *currentDB;

        /* 定义了共享的操作 */
        lktshm share;

        sqlrservercontroller *mysqlrcont;
};

#if 0
sqlrservercontroller* sqlrtranslation_lktencrypt::mysqlrcont = NULL;

static void logger_wrapper (const char* info)
{
    sqlrtranslation_lktencrypt::mysqlrcont->raiseDebugMessageEvent(info);
    return;
}
#endif

sqlrtranslation_lktencrypt::~sqlrtranslation_lktencrypt ()
{
    
}

sqlrtranslation_lktencrypt::sqlrtranslation_lktencrypt(
                    sqlrservercontroller *cont,
                    sqlrtranslations *sqlts,
                    xmldomnode *parameters) :
                sqlrtranslation(cont,sqlts,parameters) {

#if 0
    // "parameters" are the xml parameters from the config file
    debugFunction();
    
    debug=cont->getConfig()->getDebugTranslations();
    
    enabled=charstring::compareIgnoringCase(
        parameters->getAttributeValue("enabled"),"no");
#endif

    mysqlrcont = cont;
    
    currentDB = NULL;
    int nRet = 0;
    if(0 != (nRet = dzlog_init(ZLOG_SQLRELAY_CONF, ZLOG_SQLRELAY_CAT)))
    {
        zlog_profile();
        stdoutput.printf("zlog init error, ret[%d]!\n", nRet);
    }

}

/**
* @Function:debug_print
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 打印日志
* @caution: 
*/
/*
void sqlrtranslation_lktencrypt::debug_print (const char *format, ...)
{
    va_list argp;
    va_start(argp,format);
    stringbuffer msg;
    msg.append ("[lktencrypt]");
    msg.appendFormatted (format, &argp);
    va_end(argp);
    
    mysqlrcont->raiseDebugMessageEvent (msg.getString ());
}
*/

/**
* @Function:getStragegyCharset
* @Author: guojianchuan/max
* @Date: 2017-09-12
* @Description: 获取策略对应字段的字符集
* @caution: 
*/
int sqlrtranslation_lktencrypt::getStragegyCharset (COLUMN_STRATEGY_S* strategy)
{
    int charset = CHARSET_INVALID;

    if (NULL == strategy)
    {
        dzlog_error ("invalid param");
        return charset;
    }

    if (0 == strncmp ("default", strategy->charset, strlen("default")))
    {
        charset = CHARSET_UTF8;
    }
    else if (0 == strncmp ("UTF8", strategy->charset, strlen("UTF8")))
    {
        charset = CHARSET_UTF8;
    }
    else
    {
        charset = CHARSET_INVALID;
    }

    return charset;
}

/**
* @Function:findMatchColumninDBInstance
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 在数据库中获取匹配的column
* @caution: 
*/
char *sqlrtranslation_lktencrypt::findMatchColumninDBInstance 
(
    int columnIdx, 
    char* table, 
    char* db, 
    char* columnName
)
{
    if ((NULL == table) || (NULL == columnName))
    {
        return NULL;
    }

    share.readLock ();
    
    DATA_SHM_S *datashm = share.getshm();
    DATABASE_INSTANCE_S *dbInstance = &datashm->dbInstance;

    bool find = false;
    int i = 0;
    int len1 = 0;
    int len2 = 0;
    int maxLen = 0; 
    TABLE_S *tableList = NULL;
    for (i = 0; i < dbInstance->dbNum; i++)
    {
        len1 = strlen(dbInstance->dbList[i].dbName);
        len2 = strlen(db);
        maxLen = len1 > len2 ? len1 : len2;
        
        if (0 != strncmp (dbInstance->dbList[i].dbName, db, maxLen))
        {
            continue;
        }

        tableList = dbInstance->dbList[i].tableList;
        int j = 0; 
        for (j = 0; j < dbInstance->dbList[i].tableNum; j++)
        {
            len1 = strlen(tableList[j].tableName);
            len2 = strlen(table);
            maxLen = len1 > len2 ? len1 : len2;
            
            if (0 != strncmp (tableList[j].tableName, table, maxLen))
            {
                continue;
            }

            if (columnIdx < tableList[j].columnNum)
            {
                snprintf (columnName, COLUMN_MAX_NUM, "%s", 
                          tableList[j].columnList[columnIdx].columnName);
            }

            find = true;

            break;
        }

        if (true == find)
        {
            break;
        }
    }
    share.readUnLock ();

    return columnName;
}

/**
* @Function:findStrategy
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 获取strategy
* @caution: 
*/
COLUMN_STRATEGY_S* sqlrtranslation_lktencrypt::findStrategy (char* column, char* table, char *db)
{
    if ((NULL == column) || (NULL == table) || (NULL == db))
    {
        dzlog_error("Invalid param");
        return NULL;
    }
    
    dzlog_debug("find strategy, column:%s, table:%s, db:%s", column, table, db);

    share.readLock ();
    DATA_SHM_S *datashm = share.getshm();
    
    COLUMN_STRATEGY_S *ret             = NULL;
    COLUMN_STRATEGY_S *colStrategyList = datashm->appInfo.columnStrategyList;
    TABLE_STRATEGY_S  *tblStrategyList = datashm->appInfo.tblStrategyList;
    DB_STRATEGY_S     *dbStrategyList  = datashm->appInfo.dbStrategyList;
    
    int len1 = 0;
    int len2 = 0;
    int maxLen = 0;

    int l, m,n = 0;
    for (l = 0; l < datashm->appInfo.columnNum; l++)
    {
        dzlog_debug("**column:%s, tblIdx:%d, dbIdx:%d", colStrategyList[l].columnName, colStrategyList[l].tblIdx, colStrategyList[l].dbIdx);
    }
    for  (m = 0; m < datashm->appInfo.tableNum; m++)
    {
        dzlog_debug("**table:%s", tblStrategyList[m].tableName);
    }
    for (n = 0; n < datashm->appInfo.dbNum; n++)
    {
        dzlog_debug("**db:%s", dbStrategyList[n].dbName);
    }
    
    int i = 0;
    for (i = 0; i < datashm->appInfo.columnNum; i++)
    {
        len1 = strlen(column);
        len2 = strlen(colStrategyList[i].columnName);
        maxLen = len1 > len2 ? len1 : len2;
        dzlog_debug("s column:%s", colStrategyList[i].columnName);
        if (0 != strncmp (column, colStrategyList[i].columnName, maxLen))
        {
            continue;
        }
        dzlog_debug("find column");
        
        int tblIdx = colStrategyList[i].tblIdx;
        len1 = strlen(table);
        len2 = strlen(tblStrategyList[tblIdx].tableName);
        maxLen = len1 > len2 ? len1 : len2;
        dzlog_debug("s table:%s", tblStrategyList[tblIdx].tableName);
        if (0 != strncmp (table, tblStrategyList[tblIdx].tableName, maxLen))
        {
            continue;
        }
        dzlog_debug("find table");
        
        int dbIdx = colStrategyList[i].dbIdx;
        len1 = strlen(db);
        len2 = strlen(dbStrategyList[dbIdx].dbName);
        dzlog_debug("s db:%s", dbStrategyList[dbIdx].dbName);
        maxLen = len1 > len2 ? len1 : len2;
        if (0 != strncmp (db, dbStrategyList[dbIdx].dbName, maxLen))
        {
            continue;
        }
        dzlog_debug("find db");

        ret = &colStrategyList[i];
    }
    share.readUnLock ();
    
    return ret;
}

/**
* @Function:getMatchedStrategy
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 获取匹配的strategy
* @caution: 
*/
COLUMN_STRATEGY_S* sqlrtranslation_lktencrypt::getMatchedStrategy (SQLPARSER_COLUMN_NODE_S *node)
{
    COLUMN_STRATEGY_S *strategy = NULL;

    dzlog_debug("v:%s,c:%s, pf:%s, pd:%s, offset:%d, len:%d, idx:%d, match:%d",  node->value, node->column, node->prefixTable, node->prefixDatabase,
            node->offset, node->len, node->idx, node->matchPattern);
        
    if (NULL == node->column)
    {
        /* column为空，这种情况一般出现在insert into语句中 */
        if (-1 != node->idx)
        {
            char *db = NULL;
            if (NULL != node->prefixDatabase)
            {
                db = node->prefixDatabase;
            }
            else if (NULL != currentDB)
            {
                db = currentDB;
            }
            else
            {
                return NULL;
            }
        
            char columnName[COLUMN_NAME_MAX_LEN] = {0};
            if (NULL == findMatchColumninDBInstance (node->idx, 
                                                     node->prefixTable, 
                                                     db, 
                                                     columnName))
            {
                dzlog_debug("can not match column");
                return NULL;
            }
                                                     
            strategy = findStrategy (columnName, node->prefixTable, db);
        }
    }
    else
    {
        if ((NULL != node->prefixTable) && (NULL != node->prefixDatabase))
        {
            strategy = findStrategy (node->column, node->prefixTable, node->prefixDatabase);
        }
        else if ((NULL != node->prefixTable) && (NULL == node->prefixDatabase))
        {
            int possibleTblNum = node->possbileTable.tableNum;
            int i = 0;
            int len1 = 0;
            int len2 = 0;
            int maxLen = 0;
            bool find = false;
            for (i = 0; i < possibleTblNum; i++)
            {
                len1 = strlen (node->prefixTable);
                len2 = strlen (node->possbileTable.tableNode[i].tableName);
                maxLen = len1 > len2 ? len1 : len2;
                // TODO debug
                if (0 != strncmp (node->prefixTable, node->possbileTable.tableNode[i].tableName, maxLen))
                {
                    continue;
                }
        
                if (NULL != node->possbileTable.tableNode[i].dbName)
                {
                    strategy = findStrategy (node->column, node->prefixTable, node->possbileTable.tableNode[i].dbName);
                    if (NULL != strategy)
                    {
                        find = true;
                        break;
                    }
                }
                if (NULL != currentDB)
                {
                    strategy = findStrategy (node->column, node->prefixTable, currentDB);
                    if (NULL != strategy)
                    {
                        find = true;
                        break;
                    }
                }
            }

            if (false == find)
            {
                strategy = findStrategy (node->column, node->prefixTable, currentDB);
            }
        }
        else if ((NULL == node->prefixTable) && (NULL == node->prefixDatabase))
        {
            int possibleTblNum = node->possbileTable.tableNum;
            int i = 0;
            for (i = 0; i < possibleTblNum; i++)
            {
                if ((NULL != node->possbileTable.tableNode[i].dbName) && (0 != strlen(node->possbileTable.tableNode[i].dbName)))
                {
                    strategy = findStrategy (node->column, node->possbileTable.tableNode[i].tableName, node->possbileTable.tableNode[i].dbName);
                    if (NULL != strategy)
                    {
                        break;
                    }
                }
                if (NULL != currentDB)
                {
                    dzlog_debug("Currentdb:%s", currentDB);
                    strategy = findStrategy (node->column, node->possbileTable.tableNode[i].tableName, currentDB);
                    if (NULL != strategy)
                    {
                        break;
                    }
                }
            }
        }
    }

    return strategy;
}


int sqlrtranslation_lktencrypt::internal_encryptvalue (
        const std::string& input, 
        std::string& output,
        lktshm& share) 
{
    /* 采用CFB的情况下，默认不开启PADDING,所以输入和输出长度一样 */
    std::shared_ptr<unsigned char> buff(new unsigned char[input.length()]);
    
    CIPHER_AES_S cipher;
    memset (&cipher, 0, sizeof (cipher));
    snprintf (cipher.algo, sizeof (cipher.algo), "%s", AES_256_CFB);
    cipher.mode     = ENCRYPT;
    cipher.input    = (unsigned char *)input.c_str();
    cipher.inlen    = input.length();
    cipher.key      = (unsigned char *)share.getdatakey();
    cipher.iv       = (unsigned char *)share.getdataiv();
    cipher.output   = buff.get();

    int ret = LKT_AESCrypt(&cipher);
    if (ret != ERR_COMMON_SUCCESS) {
        dzlog_error("LKT_AESCrypt failed, ret:%d", ret);
        return ret;
    }

    int base64Len = cipher.outlen * 2 + 64;
    std::shared_ptr<unsigned char> b64buff(new unsigned char[base64Len]);
    if (!b64buff) {
        dzlog_error("No memory");
        return ERR_COMMON_NO_MEM;
    }
    memset (b64buff.get(), 0, base64Len);

    int cipherTextLen = 0;
    if (NULL == LKT_ConvertToBase64(cipher.output, cipher.outlen, b64buff.get(), &cipherTextLen, 0))
    {
        dzlog_error("Convert failed");
        return ERR_CONVERT_FAILED;
    }

    output.assign((const char*)b64buff.get(), cipherTextLen);
    return ERR_COMMON_SUCCESS;
}

int  sqlrtranslation_lktencrypt::normal_formatvalue(
        const std::string& b64,
        std::string& output)
{
    /* 封装成LOCKet格式的字符串 */
    int encryptedLen = b64.length() + LKT_FORMAT_LEN + 1;
    std::shared_ptr<char> encrypted(new char[encryptedLen]);
    if (!encrypted)
    {
        dzlog_error ("No memory");
        return ERR_COMMON_NO_MEM;
    }
    
    CIPHER_FORMAT_S cipherFormat;
    memset (&cipherFormat, 0, sizeof (cipherFormat));
    
    char szAppDataKey[KEY_MAX_LEN] = {0};
    snprintf (szAppDataKey, sizeof (szAppDataKey), "%s", share.getb64appdatakey());
    cipherFormat.version = VERSION_0;
    cipherFormat.mappingid = APP_CIPHER_MAPPING_ID; 
    cipherFormat.key = szAppDataKey;
    cipherFormat.cipherText = (unsigned char*)b64.c_str();
    cipherFormat.cipherLen = b64.length();
    cipherFormat.formated = (unsigned char*) encrypted.get();
    cipherFormat.formatBufLen = encryptedLen;
    
    LKT_BuildCipherFormat (&cipherFormat);

    output.assign(encrypted.get());
    
    return ERR_COMMON_SUCCESS;
}

int sqlrtranslation_lktencrypt::like_formatvalue(
    const std::string& data,
    std::string& output,
    std::string& escape) 
{
    char esc = '\\';
    if (escape.size() > 0) {
      esc = escape[0];
    }

    static std::string header = "%lkt!00__9999__";
    static std::string tailer = "%";

    std::string tmp;

    std::function<void (char)> _f = [&tmp, &esc](char c) {
      if (c == esc) {
        tmp.append(2, c);
      } else if (c == '%') {
        tmp.append(1, esc);
        tmp.append(1, c);
      } else {
        tmp.append(1, c);
      }
    };

    std::function<void (char)> _f2 = [&tmp, &esc](char c) {
      if (c == esc) {
        tmp.append(2, c);
      } else {
        tmp.append(1, c);
      }
    };
    
    std::for_each(header.begin(), header.end(), _f);
    std::for_each(data.begin(), data.end(), _f2);
    std::for_each(tailer.begin(), tailer.end(), _f);

    std::swap(tmp, output);

    return 0;    

}

int sqlrtranslation_lktencrypt::normal_encryptvalue (const std::string& input, std::string& output)
{
    if (input.empty())
    {
        dzlog_error ("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_COMMON_SUCCESS;

    std::string b64;
    ret = internal_encryptvalue(input, b64, share);
    if (ret != ERR_COMMON_SUCCESS) {
        dzlog_error ("encrypt value failed, ret:%d", ret);
        return ret;
    }

    std::string formated;
    ret = normal_formatvalue(b64, formated);
    if (ret != ERR_COMMON_SUCCESS) {
        dzlog_error ("format value failed, ret:%d", ret);
        return ret;
    }

    std::swap(formated, output);

    return ERR_COMMON_SUCCESS;
}

int sqlrtranslation_lktencrypt::search_other_encryptvalue (
    const std::string& input,
    std::string& output)
{
    if (input.empty()) {
        dzlog_error ("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    int ret = ERR_COMMON_SUCCESS;

    /* 分字 */
    std::vector<sql_field_character> words;
    ret = sql_field_trans_in_insert(input, words, CHARSET_E::CHARSET_UTF8);
    if (ret <= 0) {
        dzlog_error ("invalid string:%s",  input.c_str());
        return ERR_INVALID_STRING;
    }

    /* 分字加密 */
    std::vector<sql_field_character>::iterator it = words.begin();
    for (;it != words.end(); it++) {
        std::string b64;
        sql_field_character& word = *it; 
        ret = internal_encryptvalue(word.getData(), b64, share);
        if (ret != ERR_COMMON_SUCCESS) {
            dzlog_error ("encrypt value failed, ret:%d", ret);
            return ret;
        }
        word.setData(b64);
    }

    /* 插入 '|' */
    std::string str = sql_field_build_in_insert(words);

    /* 前后插入LKT标识符 */
    std::string formated;
    ret = normal_formatvalue(str, formated);
    if (ret != ERR_COMMON_SUCCESS) {
        dzlog_error ("format value failed, ret:%d", ret);
        return ret;
    }

    std::swap(formated, output);

    return ERR_COMMON_SUCCESS;
}

int sqlrtranslation_lktencrypt::search_like_encryptvalue (
    const std::string& input,
    std::string& output,
    std::string escape)/*这里escape是个局部对象生存周期够不着后边运行时 所以拷贝了*/
{
    if (input.empty()) {
        dzlog_error ("Invalid param");
        return ERR_COMMON_INVALID_PARAM;
    }
    int ret = ERR_COMMON_SUCCESS;

    /* 分字 */
    std::vector<sql_field_character> words;
    ret = sql_field_trans_in_like(input, words, escape, CHARSET_E::CHARSET_UTF8);
    if (ret <= 0) {
        dzlog_error ("invalid string:%s",  input.c_str());
        return ERR_INVALID_STRING;
    }

    /* 分字加密 */
    std::vector<sql_field_character>::iterator it = words.begin();
    for (;it != words.end(); it++) {
        std::string b64;
        sql_field_character& word = *it;

        if (word.getClass() != sql_field_class::DATA) {
            continue;
        }        

        ret = internal_encryptvalue(word.getData(), b64, share);
        if (ret != ERR_COMMON_SUCCESS) {
            return ret;
        }
        word.setData(b64);
    }

    /* 插入 '|' */
    std::string str = sql_field_build_in_like(words, escape);

    /* 插入LKT格式 */
    std::string formated;
    ret = like_formatvalue(str, formated, escape);
    if (ret != ERR_COMMON_SUCCESS) {
        return ret;
    }

    std::swap(formated, output);

    return ERR_COMMON_SUCCESS;
}

        
bool sqlrtranslation_lktencrypt::isContainOnlyWildCard (char* value)
{
    int len = strlen(value);

    int i = 0;

    for (i = 0; i < len; i++)
    {
        /* 先不考虑转义字符的影响 */
        if ((value[i] != '%') && (value[i] != '_'))
        {
            return false;
        }
    }

    return true;
}

sqlrtranslation_lktencrypt::encryptor sqlrtranslation_lktencrypt::getEncryptor(
    SQLPARSER_COLUMN_NODE_S *node,
    COLUMN_STRATEGY_S *strategy)
{
    encryptor encryptor;
    if (strategy->searchable) {
        if (node->isMatchPattern) {
            std::string escape = "\\";
            if (node->matchPattern.isHaveEscapeChar) {
                escape.assign(1, node->matchPattern.escapeChar);
            }
            if (node->matchPattern.matchType == LIKE) {
                if (true == isContainOnlyWildCard(node->value))
                {
                    /* do nothing */
                }
                else
                {
                    // like and not like
                    encryptor = std::bind(&sqlrtranslation_lktencrypt::search_like_encryptvalue, this, 
                            std::placeholders::_1, std::placeholders::_2, escape);
                }
            } else {
                // rlike regexp
                /* do nothing */
            }
        } else {
            // 非匹配模式，比如 name = 'jack', name > 'jack', set name = 'jack'
            encryptor = std::bind(&sqlrtranslation_lktencrypt::search_other_encryptvalue, this,
                    std::placeholders::_1, std::placeholders::_2);
        }
    } else {
        if ((node->isMatchPattern) && (node->matchPattern.matchType == OTHERS))
        {
            /* do nothing */
        }
        else
        {
            // 普通的加密
            encryptor = std::bind(&sqlrtranslation_lktencrypt::normal_encryptvalue, this,
                    std::placeholders::_1, std::placeholders::_2);
        }
    }
    return encryptor;
}

sqlrtranslation_lktencrypt::Decryptor 
  sqlrtranslation_lktencrypt::getDecryptor(const std::string& input)
{
    std::string::size_type n = input.find('|');

    if (n == std::string::npos) {
        return std::bind(
            &sqlrtranslation_lktencrypt::decrypt, 
            this, 
            std::placeholders::_1,
            std::placeholders::_2,
            share.getdatakey(),
            share.getdataiv());
    } else {
        return std::bind(
            &sqlrtranslation_lktencrypt::decrypt_searchable, 
            this, 
            std::placeholders::_1,
            std::placeholders::_2,
            share.getdatakey(),
            share.getdataiv());
    }
}

/**
* @Function:decrypt
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 加密字段数据
* @caution: 
*/
int sqlrtranslation_lktencrypt::decrypt (
    const std::string& input,
    stringbuffer& output,
    const char *datakey,
    const char *dataiv)
{
    int ret = ERR_COMMON_SUCCESS;
    /* BASE64密文转换为byte */
    int decodeLen = input.length();
    unsigned char *decodeText = new unsigned char [decodeLen];
    if (NULL == decodeText)
    {
        return ERR_COMMON_NO_MEM;
    }
    memset (decodeText, 0, decodeLen);
    
    int cipherTextLen = 0;
    if (NULL == LKT_DecodeBase64 ((unsigned char*) input.c_str(), decodeLen, decodeText, &cipherTextLen))
    {
        delete [] decodeText;
        return ERR_CONVERT_FAILED;
    }
    int plainTextLen = cipherTextLen + 1;
    unsigned char *tmpPlainText = new unsigned char[plainTextLen];
    if (NULL == tmpPlainText)
    {
        delete [] decodeText;
        return ERR_COMMON_NO_MEM;
    }
    memset (tmpPlainText, 0, plainTextLen);
    
    CIPHER_AES_S cipher;
    memset (&cipher, 0, sizeof (cipher));
    
    snprintf (cipher.algo, sizeof (cipher.algo), "%s", AES_256_CFB);
    cipher.mode     = DECRYPT;
    cipher.input    = decodeText;
    cipher.inlen    = cipherTextLen;
    cipher.key      = (unsigned char *)datakey;
    cipher.iv       = (unsigned char *)dataiv;
    cipher.output   = tmpPlainText;
    
    ret = LKT_AESCrypt(&cipher);
    if (ERR_COMMON_SUCCESS != ret)
    {
        delete [] decodeText;
        delete [] tmpPlainText;
        return ret;
    }
    output.clear ();
    output.write ((char*) tmpPlainText, cipher.outlen);
    
    delete [] decodeText;
    delete [] tmpPlainText;
    return ERR_COMMON_SUCCESS;
}

int sqlrtranslation_lktencrypt::decrypt_searchable (
    const std::string& input,
    stringbuffer& output,
    const char *datakey,
    const char *dataiv)
{
  std::vector<std::string> arr;

  std::string::size_type start = 0;
  std::string::size_type end = 0;
   while ((end = input.find('|', start)) != std::string::npos) {
    std::string substr = input.substr(start, end-start);
    start = end+1;
    arr.push_back(substr);
  }
  arr.push_back(input.substr(start, end-start));

  stringbuffer buffer;
  std::vector<std::string>::iterator it = arr.begin();
  for (;it != arr.end(); it++) {
    stringbuffer tmp;
    tmp.clear();
    int ret = decrypt(*it, tmp, datakey, dataiv);
    if (ret != ERR_COMMON_SUCCESS) {
        dzlog_error ("decrypt failed");
        return ret;
    }
    buffer.append(tmp.getString(), tmp.getSize());
  }
  std::swap(buffer, output);
  return ERR_COMMON_SUCCESS;
}

/**
* @Function:encryptAndRebuildSql
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 加密SQL字段，并且重组成新的SQL语句
* @caution: 
*/
int sqlrtranslation_lktencrypt::encryptAndRebuildSql (SQLPARSER_S *sqlparser, stringbuffer& rebuildSql)
{
    int ret = ERR_DONT_NEED_ENCRYPT;        /* 需要初始化为不需要加密 */

    /* 清理缓存 */
    rebuildSql.clear ();
    
    int i                         = 0;
    SQLPARSER_STATEMENT_S *stmt   = NULL;
    SQLPARSER_COLUMN_NODE_S *node = NULL;
    COLUMN_STRATEGY_S *strategy   = NULL;
    char *originsql               = NULL;
    int lastpostion               = 0;

    for (i = 0; i < sqlparser->statementnum; i++)
    {
        stmt = sqlparser->statement[i];

        if (NULL == stmt)
        {
            continue;
        }

        originsql = stmt->originsql;
        if (NULL == originsql)
        {
            continue;
        }

        for (node = stmt->header; node != NULL; node = node->next)
        {
            if ((node->offset < 0) || (lastpostion < 0))
            {
                dzlog_error ("invalid sql:%s, offset:%d, lastpostion:%d", originsql, node->offset, lastpostion);
                rebuildSql.clear();
                ret = ERR_INVALID_SQL;
                return ret;
            }

            /* 获取column字段匹配的strategy */
            strategy = getMatchedStrategy (node);
            if (NULL == strategy || ( NULL != strategy && 1 != strategy->state ) )
            {
                if (NULL == strategy)
                {
                    dzlog_debug ("can not find strategy");
                }
                else
                {
                    dzlog_debug ("strategy state is off");
                }

                if (node->offset - lastpostion < 0)
                {
                    dzlog_error ("invalid sql:%s, offset:%d, lastpostion:%d", 
                                originsql, node->offset, lastpostion);
                    rebuildSql.clear();
                    ret = ERR_INVALID_SQL;
                    return ret;
                }
                rebuildSql.append (originsql + lastpostion, node->offset - lastpostion);
                rebuildSql.append (node->value, node->len);
                
                lastpostion = node->offset + node->len;
              

                continue;
            }

            {
                dzlog_debug("strategyid:%d, column:%s, searchable:%d, charset:%s, state:%d, dbIdx:%d, tblIdx:%d", 
                            strategy->strategyID, strategy->columnName, strategy->searchable, 
                            strategy->charset, strategy->state,strategy->dbIdx, strategy->tblIdx);
            }
            
            if (node->len > MAX_ENCRYPT_DATA_LEN)
            {
                if (node->offset - lastpostion < 0)
                {
                    dzlog_error ("invalid sql:%s, offset:%d, lastpostion:%d", 
                                originsql, node->offset, lastpostion);
                    rebuildSql.clear();
                    ret = ERR_INVALID_SQL;
                    return ret;
                }
                rebuildSql.append (originsql + lastpostion, node->offset - lastpostion);
                rebuildSql.append (node->value, node->len);
                
                lastpostion = node->offset + node->len;
               

                dzlog_warn ("field value length large than %d K, unsupport to encrypt", MAX_ENCRYPT_DATA_LEN/1024);
                continue;
            }

            /* 检查待加密数据是否已经有LOCKET标识，避免二次加密 */
            if (1 == LKT_CheckStartIdentify(node->value))
            {
                if (node->offset - lastpostion < 0)
                {
                    dzlog_error ("invalid sql:%s, offset:%d, lastpostion:%d", 
                                originsql, node->offset, lastpostion);
                    rebuildSql.clear();
                    ret = ERR_INVALID_SQL;
                    return ret;
                }
                rebuildSql.append (originsql + lastpostion, node->offset - lastpostion);
                rebuildSql.append (node->value, node->len);
                
                lastpostion = node->offset + node->len;
        
                dzlog_warn ("field value has been encrypted, dont't need to ecnrypt agin");
                continue;
            }

            /* 当前字段有加密策略, 则先置为成功 */
            ret = ERR_COMMON_SUCCESS;

            /* 解密datakey,dataiv */
            ret = share.decodeDataKeyIv ((unsigned char*) share.getb64appdatakey(), (unsigned char*) share.getb64appdataiv());
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error ("decodeDataKeyIv error ");
                rebuildSql.clear();
                return ret;
            }

            encryptor encryptor = getEncryptor(node, strategy);
            if (!encryptor) {
                if (node->offset - lastpostion < 0)
                {
                    dzlog_error ("invalid sql:%s, offset:%d, lastpostion:%d", 
                                originsql, node->offset, lastpostion);
                    rebuildSql.clear();
                    ret = ERR_INVALID_SQL;
                    return ret;
                }
                rebuildSql.append (originsql + lastpostion, node->offset - lastpostion);
                rebuildSql.append (node->value, node->len);
                lastpostion = node->offset + node->len;
                continue;
            }

            dzlog_debug ("do encrypt");
            std::string input(node->value, node->len);
            std::string encrypted;
            ret = encryptor(input, encrypted);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error ("encrypt value error");
                rebuildSql.clear();
                return ret;
            }

            if (node->offset - lastpostion < 0)
            {
                dzlog_error ("invalid sql:%s, offset:%d, lastpostion:%d", 
                        originsql, node->offset, lastpostion);
                rebuildSql.clear();
                ret = ERR_INVALID_SQL;
                return ret;
            }

            /* 将加密的数据进行重组 */
            rebuildSql.append (originsql + lastpostion, node->offset - lastpostion);
            rebuildSql.append (encrypted.c_str(), encrypted.length());
            
            lastpostion = node->offset + node->len;
            
        }
      
        rebuildSql.append (originsql + lastpostion, strlen(originsql) - lastpostion);
    }

    return ret;
}

/**
* @Function:decryptAndRebuildSql
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 解密SQL字段，并且重组成新的SQL语句
* @caution: 
*/
int sqlrtranslation_lktencrypt::decryptAndRebuildSql (SQLPARSER_S *sqlparser, stringbuffer& rebuildSql)
{
    int ret = ERR_DONT_NEED_ENCRYPT;        /* 需要初始化为不需要加密 */

    /* 清理缓存 */
    rebuildSql.clear ();
    
    int i                         = 0;
    SQLPARSER_STATEMENT_S *stmt   = NULL;
    SQLPARSER_COLUMN_NODE_S *node = NULL;
    COLUMN_STRATEGY_S *strategy   = NULL;
    char *originsql               = NULL;
    int lastpostion               = 0;

    for (i = 0; i < sqlparser->statementnum; i++)
    {
        stmt = sqlparser->statement[i];

        if (NULL == stmt)
        {
            continue;
        }

        originsql = stmt->originsql;
        if (NULL == originsql)
        {
            continue;
        }

        for (node = stmt->header; node != NULL; node = node->next)
        {
            /* 检查待解密数据是否有LOCKET标识*/
            if (0 == LKT_CheckStartIdentify(node->value))
            {
                if (node->offset - lastpostion < 0)
                {
                    rebuildSql.clear();
                    ret = ERR_INVALID_SQL;
                    dzlog_error ("invalid sql:%s, offset:%d, lastpostion:%d", 
                                originsql, node->offset, lastpostion);
                    return ret;
                }
                rebuildSql.append (originsql + lastpostion, node->offset - lastpostion);
                rebuildSql.append (node->value, node->len);
                
                lastpostion = node->offset + node->len;
        
                continue;
            }

            /* 检查密文格式是否正确 */
            ret = LKT_CheckCipherFormat(node->value, share.getb64appdatakey());
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error("Invalid cipher format:%s", node->value);
                return ret;
            }

            /* 解密datakey,dataiv */
            ret = share.decodeDataKeyIv ((unsigned char*) share.getb64appdatakey(), (unsigned char*) share.getb64appdataiv());
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error ("decodeDataKeyIv error ");
                rebuildSql.clear();
                return ret;
            }

            int len = strlen(node->value) + 1;
            char *cipherText = new char[node->len];
            if (NULL == cipherText)
            {
                dzlog_error ("no memory");
                return ERR_COMMON_NO_MEM;
            }
            memset (cipherText, 0, len);

            /* 获取真正的密文 */
            LKT_GetCipherText(node->value, cipherText);

            /* 解密密文 */
            std::string input(cipherText);
            delete [] cipherText;

            Decryptor decryptor = getDecryptor(input);

            decryptoutput.clear();	
            ret = decryptor(input, decryptoutput);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_error ("decrypt failed, cipher text:%s", node->value);
                return ret;
            }

            /* 将解密的数据进行重组 */
            if (node->offset - lastpostion < 0)
            {
                rebuildSql.clear();
                ret = ERR_INVALID_SQL;
                dzlog_error ("invalid sql:%s, offset:%d, lastpostion:%d", 
                        originsql, node->offset, lastpostion);
                return ret;
            }
            rebuildSql.append (originsql + lastpostion, node->offset - lastpostion);
            rebuildSql.append (decryptoutput.getString(), decryptoutput.getSize());
            
            lastpostion = node->offset + node->len;
            
        }
      
        rebuildSql.append (originsql + lastpostion, strlen(originsql) - lastpostion);
    }

    return ret;
}

/**
* @Function:isExistCharTypeValue
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 判断SQL结构中是否存在char类型的节点
* @caution: 
*/
bool sqlrtranslation_lktencrypt::isExistCharTypeValue (SQLPARSER_S *sqlparser)
{
    bool needEnc = false;
    
    if (0 == sqlparser->statementnum)
    {
        return needEnc;
    }

    int i                       = 0;
    SQLPARSER_STATEMENT_S *stmt = NULL;
    
    for (i = 0; i < sqlparser->statementnum; i++)
    {
        stmt = sqlparser->statement[i];
        if (NULL == stmt)
        {
            continue;
        }

        if (0 == stmt->nodeNum)
        {
            continue;
        }

        needEnc = true;
        
        break;
    }

    return needEnc;
}

/**
* @Function:isSupportedStatement
* @Author: guojianchuan/max
* @Date: 2017-09-09
* @Description: 判断是否是支持解析的SQL语句
* @caution: 
*/
bool sqlrtranslation_lktencrypt::isSupportedStatement (const char* sql)
{
    bool support   = false;
    int i          = 0;
    int supportNum = sizeof (supportStatement) / sizeof (supportStatement[0]);

    /* 若SQL是以 '/''*' 注释开头的, 则继续进行SQL解析 */
    if (strlen(sql) >= 2)
    {
        if (('/' == sql[0]) && (('*' == sql[1])))
        {
            return true;
        }
    }
    
    /* 若SQL不是已支持的SQL语法，就不再继续进行SQL解析, 提升性能 */
    for (i = 0; i < supportNum; i++)
    {
        if (0 == strncasecmp (sql, supportStatement[i], strlen(supportStatement[i])))
        {
            support = true;
            break;
        }
    }

    return support;
}

/**
* @Function:isDataRefreshDecryptIdentify
* @Author: guojianchuan/max
* @Date: 2017-09-22
* @Description: 检查数据刷新解密的标识
* @caution: 
*/
bool sqlrtranslation_lktencrypt::isDataRefreshDecryptIdentify (const char* sql)
{
    if (0 == strncasecmp (sql, DATA_REFRESH_DECRYPT_IDENTIFY, strlen(DATA_REFRESH_DECRYPT_IDENTIFY)))
    {
        return true;
    }

    return false;
}

/**
* @Function:isSignleDataDecryptIdentify
* @Author: guojianchuan/max
* @Date: 2017-09-22
* @Description: 是否是单个数据解密
* @caution: 
*/
bool sqlrtranslation_lktencrypt::isSignleDataDecryptIdentify (const char* sql)
{
    if (0 == strncasecmp (sql, DATA_REFRESH_SIGNLE_DATADECRYPTY, strlen(DATA_REFRESH_SIGNLE_DATADECRYPTY)))
    {
        return true;
    }

    return false;
}

/**
* @Function:run
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 当前插件的入口函数
* @caution: 
*/
bool sqlrtranslation_lktencrypt::run(sqlrserverconnection *sqlrcon,
                    sqlrservercursor *sqlrcur,
                    const char *query,
                    stringbuffer *translatedquery) {
                    
    int ret = ERR_COMMON_SUCCESS;

    /* 加载sqlrelay默认的格式化SQL语句模块normalize后, translatequery出参在进入run后值不为空,所以需要清空 */
    translatedquery->clear();

    /* 对于不支持加密的SQL语句，不继续执行 */
    if (false == isSupportedStatement (query))
    {
        dzlog_debug("[SQL]: %s",query);
        return false;
    }
    else
    {
        dzlog_debug("SQL: %s",query);
    }
    
    /* 共享内存模块若没有初始化完成，不继续执行 */
    if (false == share.init ())
    {
        return false;
    }

    /* 暂不支持SQL语句中包含\字符的加密 */
    if (NULL != strchr (query, '\\'))
    {
        return false;
    }

    /* 解密刷新的时候，进来的数据本身就是明文，所以不需要再次解析SQL和解密 */
    int dr_decrypt = isDataRefreshDecryptIdentify (query);
    if (true == dr_decrypt)
    {
        return false;
    }

    /* 记录当前连接使用的db, 当客户端使用 "use db名称" SQL语句时, 这个值就会被记录 */
    currentDB = sqlrcon->getCurrentDatabase ();
    
    SQLPARSER_S sqlparser;
    stringbuffer rebuildSql;
    memset (&sqlparser, 0, sizeof (sqlparser));
    
    /* 解析SQL语句 */
    do 
    {
        dzlog_debug("Parse sql");
        ret = LKT_SQLPARSER_ParseSQL (0, query, &sqlparser, NULL);
        if (ERR_COMMON_SUCCESS != ret)
        {
            dzlog_error ("LKT_SQLPARSER_ParseSQL failed. SQL: %s", query);
            break;
        }
        
        /* DBS只加密char字段类型，如果不存在char类型，则不加密 */
        if (false == isExistCharTypeValue (&sqlparser))
        {
            dzlog_debug("No char column");
            ret = ERR_DONT_NEED_ENCRYPT;
            break;
        }

        int signle_decrypt = isSignleDataDecryptIdentify (query);
        if (true == signle_decrypt)
        {
            /* 解密&&重组SQL语句 */
            ret = decryptAndRebuildSql(&sqlparser, rebuildSql);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_debug("decrypt and rebuild failed, ret:%d", ret);
                break;
            }
        }
        else
        {
            /* 加密&&重组SQL语句 */
            ret = encryptAndRebuildSql(&sqlparser, rebuildSql);
            if (ERR_COMMON_SUCCESS != ret)
            {
                dzlog_debug("encrypt and rebuild failed, ret:%d", ret);
                break;
            }
        }
        
    } while (0);

    /* 释放资源 */
    LKT_SQLPARSER_Release (&sqlparser);

    if (ERR_COMMON_SUCCESS != ret)
    {
        if (ERR_DONT_NEED_ENCRYPT == ret)
        {
            dzlog_debug("Do not need encrypt");
        }
        else
        {
            dzlog_error("Encrypt sql failed");
        }

        return false;
    }

    if (0 == rebuildSql.getSize ())
    {
        /* rebuild等于0，说明SQL语句中有char类型的字段，但是都不需要加密 */
        translatedquery->write(query);        
    }
    else
    {
        translatedquery->write(rebuildSql.getString());        
    }

    dzlog_debug("Rebuild SQL: %s",translatedquery->getString());

    /* 返回true，表示输入的SQL语句已经被处理，处理后的SQL语句存放在translatedquery中 */
    return true;
}

extern "C" {
    SQLRSERVER_DLLSPEC sqlrtranslation *new_sqlrtranslation_lktencrypt(
                        sqlrservercontroller *cont,
                        sqlrtranslations *sqlts,
                        xmldomnode *parameters) {
        return new sqlrtranslation_lktencrypt(cont,sqlts,parameters);
    }
}
